<template>
  <n-card
    size="small"
    :bordered="false"
    content-style="padding: 0px"
    :segmented="{
      content: true,
      footer: 'soft',
    }">
    <template #header>
      <div style="font-size: 14px" class="flex-y-c">
        <n-icon size="20" color="grey" style="margin-right: 4px">
          <PlugConnected20Filled />
        </n-icon>
        我的连接
      </div>
    </template>
    <template #header-extra>
      <n-dropdown
        trigger="click"
        size="small"
        placement="right-start"
        :options="
          ['MySQL', 'PostgreSQL'].map((name) => {
            const key = name.toLowerCase();
            return {
              label: name,
              key,
              icon: renderSvgIcon(key),
            };
          })
        "
        @select="handleSelectConnType">
        <n-button text type="primary">
          <n-icon size="20"><AddCircle16Filled /></n-icon>
        </n-button>
      </n-dropdown>
    </template>

    <n-tree
      block-line
      :data="data"
      :node-props="nodeProps"
      virtual-scroll
      style="max-height: calc(100vh - 50px)" />
    <n-dropdown
      :show="menuTools.visible"
      :x="menuTools.x"
      :y="menuTools.y"
      trigger="manual"
      size="small"
      :placement="'bottom-start'"
      :options="[
        {
          label: '属性',
          key: 'config',
          icon: renderIcon(CalendarSettings20Regular),
        },
        {
          label: '刷新',
          key: 'reload',
          icon: renderIcon(ArrowClockwise20Regular),
        },
        {
          label: '删除',
          key: 'remove',
          icon: renderIcon(DeleteDismiss20Regular),
        },
      ]"
      @select="handleMenuToolsSelect"
      @clickoutside="menuTools.visible = false" />
  </n-card>
</template>

<script lang="tsx" setup>
import {
  NButton,
  NCheckbox,
  NDropdown,
  NFlex,
  NIcon,
  TreeOption,
} from "naive-ui";
import {
  PlugConnected20Filled,
  AddCircle16Filled,
  CalendarSettings20Regular,
  ArrowClockwise20Regular,
  DeleteDismiss20Regular,
} from "@vicons/fluent";
import SvgIcon from "@/components/SvgIcon.vue";
import ConnForm from "./ConnForm.vue";
import { dataSourceMap } from "./constant";
import { context } from "@/storage/context";
import { getDatabases } from "@/api";

const emits = defineEmits<{
  (e: "select-table", option: MenuOption): void;
}>();

const dataSourceConfig = ref(context.getDataSourceConfig() || {});

const handleCacheConfig = () => {
  context.setDataSourceConfig(dataSourceConfig.value);
};

const renderIcon = (IconComp: any) => {
  return () => (
    <NIcon>
      <IconComp />
    </NIcon>
  );
};

const renderSvgIcon = (type: keyof typeof dataSourceMap, size?: number) => {
  return () => (
    <SvgIcon name={type} color={dataSourceMap[type].color} size={size} />
  );
};

const renderDatabaseSelectItem = (db: string, shows: string[]) => {
  return () => (
    <NFlex align="center" className="table-select-item">
      <NCheckbox
        checked={shows.includes(db)}
        onUpdateChecked={(checked: boolean) => {
          if (checked) shows.push(db);
          else shows.splice(shows.indexOf(db), 1);
          shows.sort();
          handleCacheConfig();
        }}
      />
      {db}
    </NFlex>
  );
};

const menuTools = ref({ visible: false, x: 0, y: 0 });
let currMenuOption: MenuOption | null = null;
const nodeProps = ({ option }: { option: TreeOption }) => {
  return {
    onclick: () => {
      if (option.table) emits("select-table", option as MenuOption);
    },
    onContextmenu: (e: MouseEvent) => {
      if (!option.children) return;
      e.preventDefault();

      currMenuOption = option as MenuOption;
      menuTools.value = {
        visible: true,
        x: e.clientX,
        y: e.clientY,
      };
    },
  };
};
const handleMenuToolsSelect = async (key: string) => {
  menuTools.value.visible = false;
  if (key === "config") {
    const formData = JSON.parse(JSON.stringify(currMenuOption!.target.info));
    const { form, data } = await showConnForm(formData);
    currMenuOption!.target.info = form;
    currMenuOption!.target.databases = data;
    currMenuOption!.target.shows = currMenuOption!.target.shows.filter((db) =>
      data.includes(db)
    );
    if (currMenuOption!.key !== form.name) {
      dataSourceConfig.value[form.name!] = currMenuOption!.target;
      delete dataSourceConfig.value[currMenuOption!.key!];
    }
  } else if (key === "reload") {
    const { data } = await getDatabases(currMenuOption!.target.info);
    currMenuOption!.target.databases = data;
    currMenuOption!.target.shows = currMenuOption!.target.shows.filter((db) =>
      data.includes(db)
    );
  } else if (key === "remove") {
    window.$dialog?.warning({
      title: "确定删除连接？",
      positiveText: "确定",
      negativeText: "取消",
      onPositiveClick: async () => {
        delete dataSourceConfig.value[currMenuOption!.key!];
        dataSourceConfig.value = Object.assign({}, dataSourceConfig.value);
      },
    });
  }
  handleCacheConfig();
};

const data = computed(() => {
  const menuData = [];
  for (const name of Object.keys(dataSourceConfig.value)) {
    const conf = dataSourceConfig.value[name];
    const menuItem = {
      label: name,
      key: name,
      icon: renderSvgIcon(conf.info.type!),
      children: conf.shows.map((db) => ({
        label: db,
        key: `${name}-${db}`,
        target: conf,
        table: true,
      })),
      target: conf,
      prefix: renderSvgIcon(conf.info.type!),
      suffix: () => (
        <NDropdown
          trigger="click"
          placement="bottom-start"
          scrollable
          size="small"
          options={conf.databases.map((db) => ({
            key: db,
            type: "render",
            render: renderDatabaseSelectItem(db, conf.shows),
          }))}>
          <NButton ghost={true} size="tiny">
            {`${conf.shows.length} of ${conf.databases.length}`}
          </NButton>
        </NDropdown>
      ),
    };
    menuData.push(menuItem);
  }
  return menuData;
});

const handleSelectConnType = async (type: string) => {
  const formData = JSON.parse(JSON.stringify(dataSourceMap[type].defaultForm));
  const { form, data } = await showConnForm(formData);
  dataSourceConfig.value[form.name!] = {
    info: form,
    databases: data,
    shows: [],
  };
  // 排序对象
  const ordered = new Map();
  Object.keys(dataSourceConfig.value).forEach((key) =>
    ordered.set(key, dataSourceConfig.value[key])
  );
  dataSourceConfig.value = Object.fromEntries(ordered);
  handleCacheConfig();
};

const showConnForm = (formData: DataSourceInfo) => {
  return new Promise<{ form: DataSourceInfo; data: string[] }>((resolve) => {
    const formRef = ref<InstanceType<typeof ConnForm>>();
    window.$dialog?.create({
      title: "新建连接",
      titleStyle: "font-size: 16px",
      contentStyle: "padding-top: 20px",
      icon: renderSvgIcon(formData.type!, 20),
      content: () => <ConnForm ref={formRef} data={formData} />,
      positiveText: "确认",
      negativeText: "算了",
      onPositiveClick: async () => {
        const data = await formRef.value!.handleConnect();
        resolve(data!);
      },
    });
  });
};
</script>

<style>
.table-select-item {
  padding: 5px 20px;
  cursor: default;
}
.table-select-item:hover {
  background-color: #f5f5f5;
}

.n-dropdown-menu .n-scrollbar-container {
  max-height: 500px;
}
</style>
